import React, { useEffect, useMemo, useState } from "react";
import "./transfer.scss";
import { CheckboxGroup, Checkbox } from "../checkbox/checkbox-group";
import Button from "../button/button";
import Input from "../input/input";

/**
 * @param key 唯一标识
 * @param label 显示的文本
 * @param disabled 是否禁用
 */
interface Option {
    key: number | string
    label: string
    disabled: boolean
}

/**
 * @param data Transfer 的数据源
 * @param filterable 是否可搜索
 * @param filterPlaceholder 搜索框占位符
 * @param targetOrder 右侧列表元素的排序策略： 若为 original，则保持与数据源相同的顺序； 若为 push，则新加入的元素排在最后； 若为 unshift，则新加入的元素排在最前
 * @param titles 自定义列表标题
 * @param leftDefaultChecked 初始状态下左侧列表的已勾选项的 key 数组
 * @param rightDefaultChecked 初始状态下右侧列表的已勾选项的 key 数组
 * @param change 左右交换时触发的函数
 * @param leftCheckChange 左侧选中值改变时触发的函数
 * @param rightCheckChange 右侧选中值改变时触发的函数
 */
interface Props {
    data?: Array<Option>,
    filterable?: boolean,
    filterPlaceholder?: string,
    targetOrder?: 'original' | 'push' | 'unshift',
    titles?: [string, string],
    leftDefaultChecked?: Array<string | number>,
    rightDefaultChecked?: Array<string | number>,
    change?: (value: Array<Option>) => void,
    leftCheckChange?: (value: Array<Option>, value1: Array<string | number>) => void,
    rightCheckChange?: (value: Array<Option>, value1: Array<string | number>) => void,
    leftFooter?: React.ReactNode,
    rightFooter?: React.ReactNode,
    defaultValue?: Array<string | number>
}

const Transfer = ({
    data = [],
    filterable = false,
    filterPlaceholder,
    targetOrder = "original",
    titles = ["List1", "List2"],
    leftDefaultChecked = [],
    rightDefaultChecked = [],
    change = () => { },
    leftCheckChange = () => { },
    rightCheckChange = () => { },
    leftFooter,
    rightFooter,
    defaultValue = []
}: Props) => {
    // 是否选择全部
    const [checkAll1, setCheckAll1] = useState<boolean>(false)
    const [checkAll2, setCheckAll2] = useState<boolean>(false)
    // 不确定状态
    const [leftIndeterminate, setLeftIndeterminate] = useState<boolean>(false)
    const [rightIndeterminate, setRightIndeterminate] = useState<boolean>(false)

    // 左侧数据
    const [leftData, setLeftData] = useState<Array<Option>>([])
    const [leftValue, setLeftValue] = useState<Array<string | number>>([])

    // 右侧数据
    const [rightData, setRightData] = useState<Array<Option>>([])
    const [rightValue, setRightValue] = useState<Array<string | number>>([])

    useEffect(() => {
        let leftArr: Array<Option> = []
        let rightArr: Array<Option> = []
        data.forEach(item => {
            if (defaultValue.includes(item.key)) {
                rightArr.push(item)
            } else {
                leftArr.push(item)
            }
        })
        setLeftData(leftArr)
        setRightData(rightArr)
        setLeftValue(leftDefaultChecked)
        setRightValue(rightDefaultChecked)
    }, [data])

    // 搜索
    const [leftSearch, setLeftSearch] = useState<string>("")
    const [rightSearch, setRightSearch] = useState<string>("")

    // 页面显示的数据列表
    const leftList = useMemo(() => {
        const list = leftData.filter(item => item.label.toLocaleLowerCase().includes(leftSearch.toLocaleLowerCase()))
        let count: number = 0
        let count1: number = 0
        list.forEach(item => {
            if (!item.disabled) {
                count++
                if (leftValue.includes(item.key)) count1++
            }
        })
        setCheckAll1(count === count1)
        setLeftIndeterminate(count1 > 0 && count1 < count)
        return list
    }, [leftSearch, leftData])

    const rightList = useMemo(() => {
        const list = rightData.filter(item => item.label.toLocaleLowerCase().includes(rightSearch.toLocaleLowerCase()))
        let count: number = 0
        let count1: number = 0
        list.forEach(item => {
            if (!item.disabled) {
                count++
                if (rightValue.includes(item.key)) count1++
            }
        })
        setCheckAll2(count === count1)
        setRightIndeterminate(count1 > 0 && count1 < count)
        return list
    }, [rightSearch, rightData])

    const changeLeft = (val: boolean) => {
        let arr: Array<string | number> = []
        let arr1: Array<Option> = []
        leftList.forEach(item => {
            if (!item.disabled) {
                arr.push(item.key)
                arr1.push(item)
            }
        })
        setLeftValue(val ? arr : [])
        setLeftIndeterminate(false)
        setCheckAll1(val)
        leftCheckChange(val ? arr1 : [], val ? arr : [])
    }

    const changeLeft1 = (val: Array<string | number>) => {
        let count: number = 0
        let count1: number = 0
        leftList.forEach(item => {
            if (!item.disabled) {
                count++
                if (val.includes(item.key)) {
                    count1++
                }
            }
        })
        setCheckAll1(count1 === count)
        setLeftIndeterminate(count1 > 0 && count1 < count)
        let arr: Array<Option> = []
        leftData.forEach(item => {
            if (val.includes(item.key)) {
                arr.push(item)
            }
        })
        leftCheckChange(arr, val)
    }

    const changeRight = (val: boolean) => {
        let arr: Array<string | number> = []
        let arr1: Array<Option> = []
        rightList.forEach(item => {
            if (!item.disabled) {
                arr.push(item.key)
                arr1.push(item)
            }
        })
        setRightValue(val ? arr : [])
        setRightIndeterminate(false)
        setCheckAll2(val)
        rightCheckChange(val ? arr1 : [], val ? arr : [])
    }

    const changeRight1 = (val: Array<string | number>) => {
        let count: number = 0
        let count1: number = 0
        rightList.forEach(item => {
            if (!item.disabled) {
                count++
                if (val.includes(item.key)) {
                    count1++
                }
            }
        })
        setCheckAll2(count1 === count)
        setRightIndeterminate(count1 > 0 && count1 < count)
        let arr: Array<Option> = []
        rightData.forEach(item => {
            if (val.includes(item.key)) {
                arr.push(item)
            }
        })
        rightCheckChange(arr, val)
    }

    const toLeft = () => {
        let arr: Array<Option> = []
        let arr1: Array<Option> = []
        if (targetOrder === 'push') {
            leftData.forEach(item => {
                arr.push(item)
            })
            data.forEach(item => {
                if (rightValue.includes(item.key)) {
                    arr.push(item)
                }
            })
        } else if (targetOrder === 'unshift') {
            leftData.forEach(item => {
                arr.push(item)
            })
            data.forEach(item => {
                if (rightValue.includes(item.key)) {
                    arr.unshift(item)
                }
            })
        } else {
            data.forEach(item => {
                if (rightValue.includes(item.key)) {
                    arr.push(item)
                    return
                }
                for (let item1 of leftData) {
                    if (item1.key === item.key) {
                        arr.push(item)
                        break
                    }
                }
            })
        }
        data.forEach(item => {
            if (!rightValue.includes(item.key)) {
                for (let item1 of rightData) {
                    if (item1.key === item.key) {
                        arr1.push(item)
                        break
                    }
                }
            }
        })
        setLeftData(arr)
        setRightData(arr1)
        setRightValue([])
        setLeftIndeterminate(false)
        setRightIndeterminate(false)
        setCheckAll1(false)
        setCheckAll2(false)
        let arr2: Array<string | number> = []
        arr1.forEach(item => {
            arr2.push(item.key)
        })
        change(arr1)
    }

    const toRight = () => {
        let arr: Array<Option> = []
        let arr1: Array<Option> = []
        if (targetOrder === 'push') {
            rightData.forEach(item => {
                arr.push(item)
            })
            data.forEach(item => {
                if (leftValue.includes(item.key)) {
                    arr.push(item)
                }
            })
        } else if (targetOrder === 'unshift') {
            rightData.forEach(item => {
                arr.push(item)
            })
            data.forEach(item => {
                if (leftValue.includes(item.key)) {
                    arr.unshift(item)
                }
            })
        } else {
            data.forEach(item => {
                if (leftValue.includes(item.key)) {
                    arr.push(item)
                    return
                }
                for (let item1 of rightData) {
                    if (item1.key === item.key) {
                        arr.push(item)
                        break
                    }
                }
            })
        }
        data.forEach(item => {
            if (!leftValue.includes(item.key)) {
                for (let item1 of leftData) {
                    if (item1.key === item.key) {
                        arr1.push(item)
                        break
                    }
                }
            }
        })
        setRightData(arr)
        setLeftData(arr1)
        setLeftValue([])
        setLeftIndeterminate(false)
        setRightIndeterminate(false)
        setCheckAll1(false)
        setCheckAll2(false)
        let arr2: Array<string | number> = []
        arr.forEach(item => {
            arr2.push(item.key)
        })
        change(arr)
    }

    return (
        <div className="pe-transfer">
            <div className="pe-transfer-content">
                <div className="pe-transfer-header">
                    <Checkbox value="1" label={titles[0]} indeterminate={leftIndeterminate} defaultValue={checkAll1}
                        onChange={changeLeft} />
                    <div className="pe-transfer-header-font">{leftValue.length}/{leftData.length}</div>
                </div>
                {
                    filterable ? (
                        <div className="pe-transfer-search">
                            <Input size="small" placeholder={filterPlaceholder} onInput={(e) => {
                                setLeftSearch((e.target as HTMLInputElement).value)
                            }} />
                        </div>
                    ) : null
                }
                <div className="pe-transfer-main">
                    {
                        leftData.length > 0 ? (
                            <CheckboxGroup defaultValue={leftDefaultChecked} value={leftValue}
                                change={(value) => {
                                    changeLeft1(value as Array<string | number>)
                                    setLeftValue(value as Array<string | number>)
                                }}>
                                {
                                    leftList.map(item => (
                                        <Checkbox key={item.key} className="pe-transfer-checkbox" disabled={item.disabled}
                                            value={item.key}>
                                            <span>{item.label}</span>
                                        </Checkbox>
                                    ))
                                }
                            </CheckboxGroup>
                        ) : (
                            <div className="pe-no-data">暂无数据</div>
                        )
                    }
                </div>
                {
                    !(!leftFooter && !rightFooter) ? (
                        <div className="pe-transfer-footer">
                            {leftFooter}
                        </div>
                    ) : null
                }
            </div>
            <Button type="primary" className="pe-transfer-left-btn" disabled={rightValue.length === 0} onClick={toLeft}>
                <i className="pe-icon">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
                        <path fill="currentColor"
                            d="M609.408 149.376 277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0 30.592 30.592 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.592 30.592 0 0 0 0-42.688 29.12 29.12 0 0 0-41.728 0z">
                        </path>
                    </svg>
                </i>
            </Button>
            <Button type="primary" className="pe-transfer-right-btn" disabled={leftValue.length === 0} onClick={toRight}>
                <i className="pe-icon">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
                        <path fill="currentColor"
                            d="M340.864 149.312a30.592 30.592 0 0 0 0 42.752L652.736 512 340.864 831.872a30.592 30.592 0 0 0 0 42.752 29.12 29.12 0 0 0 41.728 0L714.24 534.336a32 32 0 0 0 0-44.672L382.592 149.376a29.12 29.12 0 0 0-41.728 0z">
                        </path>
                    </svg>
                </i>
            </Button>
            <div className="pe-transfer-content">
                <div className="pe-transfer-header">
                    <Checkbox value="1" label={titles[1]} indeterminate={rightIndeterminate} defaultValue={checkAll2}
                        onChange={changeRight} />
                    <div className="pe-transfer-header-font">{rightValue.length}/{rightData.length}</div>
                </div>
                {
                    filterable ? (
                        <div className="pe-transfer-search">
                            <Input size="small" placeholder={filterPlaceholder} onInput={(e) => {
                                setRightSearch((e.target as HTMLInputElement).value)
                            }} />
                        </div>
                    ) : null
                }
                <div className="pe-transfer-main">
                    {
                        rightData.length > 0 ? (
                            <CheckboxGroup defaultValue={rightDefaultChecked} value={rightValue}
                                change={(value) => {
                                    changeRight1(value as Array<string | number>)
                                    setRightValue(value as Array<string | number>)
                                }}>
                                {
                                    rightList.map(item => (
                                        <Checkbox key={item.key} className="pe-transfer-checkbox" disabled={item.disabled}
                                            value={item.key}>
                                            <span>{item.label}</span>
                                        </Checkbox>
                                    ))
                                }
                            </CheckboxGroup>
                        ) : (
                            <div className="pe-no-data">暂无数据</div>
                        )
                    }
                </div>
                {
                    !(!leftFooter && !rightFooter) ? (
                        <div className="pe-transfer-footer">
                            {rightFooter}
                        </div>
                    ) : null
                }
            </div>
        </div>
    )
}

export default Transfer